package model

import (
	"fmt"
	"gogame/game/manage/config"
	"gogame/game/manage/module"
	"gogame/gameconfig"
	"gogame/lib"
	"gogame/lib/database/mongo"
	myRedis "gogame/lib/database/redis"
	"gogame/logger"
	"strings"
	"sync"
	"time"
)

// 这里的单独使用 g.GC会导致循环引用
var (
	Redis      = myRedis.InitRedis()
	CrossRedis = myRedis.InitCrossRedis()
	GC         = config.Manage
	M          = module.Manage
	C          = lib.Common
	Mdb        = mongo.InitMongo()
	CrossMdb   = mongo.InitCrossMongo()
	Event      = lib.GlobalEvent
)

type models struct {
	// 跨服model
	CrossUserInfo *CrossUserInfo

	// 本服model
	GameLog  *GameLog
	UserInfo *UserInfo
	Hero     *Hero
	Item     *Item
	Task     *Task
}

type ManageStruct struct {
	*models // 所有model集合

	/*
		uid2Mode2Info = {
			"uid1": {
				"userinfo": ModelInfo
				"hero": ModelInfo
			}
			"uid2": {
				"userinfo": ModelInfo
				"hero": ModelInfo
			}
		}
	*/
	uid2DbName2ModelBase map[string]map[string]*ModelBase // model过期处理
	mu                   sync.RWMutex

	modelLogManage      *ModelLogManage // modelLog管理
	modelLogCrossManage *ModelLogManage // modelLog跨服管理
}

var Manage *ManageStruct

func init() {
	Manage = &ManageStruct{
		models:               &models{},
		uid2DbName2ModelBase: make(map[string]map[string]*ModelBase),
	}

	// 跨服默认开启 都可以访问cross
	Manage.modelLogCrossManage = InitModelLogManage("cross")
	// 非跨服才启动本服modelLog
	if gameconfig.ServerConfig.GetGameVer() != "cross" {
		Manage.modelLogManage = InitModelLogManage("local")
	}

	// 数据过期清除
	go Manage.CheckClearModel()
}

// ClearModelByUid 清除指定玩家数据
func (m *ManageStruct) ClearModelByUid(uid string) {
	m.mu.RLock()
	_db2ModelBase, ok := m.uid2DbName2ModelBase[uid]
	m.mu.RUnlock()

	// 当前worker没有此玩家数据
	if !ok {
		return
	}

	_redisClear, _crossRedisClear := false, false
	var _delDbNameList []string
	for dbName, modelBase := range _db2ModelBase {
		// 跨服redis
		if strings.Contains(dbName, "cross") {
			if !_crossRedisClear {
				_crossRedisClear = true
			}
		} else {
			// 本服redis
			if !_redisClear {
				_redisClear = true
			}
		}
		_delDbNameList = append(_delDbNameList, dbName)
		modelBase.Clear(uid)
	}

	// 管道批量操作
	if _redisClear {
		Redis.Pipeline.Exec()
	}
	if _crossRedisClear {
		CrossRedis.Pipeline.Exec()
	}

	// 内存数据清除
	m.mu.Lock()
	delete(m.uid2DbName2ModelBase, uid)
	m.mu.Unlock()

	//logger.Print("玩家【%s】model清除成功--->%v", uid, _delDbNameList)

}

// clearModel 清除过期model
func (m *ManageStruct) clearModel() {
	// 没有需要处理的数据
	if len(m.uid2DbName2ModelBase) == 0 {
		return
	}

	defer func() {
		if err := recover(); err != nil {
			logger.WorkerLog.Warn(fmt.Sprintf("model.clearModel fail!!\n%s", err))
		}
	}()
	m.mu.RLock()
	_nt := C.Now()
	_delUid2DbNameList := make(map[string][]string) // 需要清除的玩家的db
	_redisClear, _crossRedisClear := false, false
	for uid, dbName2ModelInfo := range m.uid2DbName2ModelBase {
		for dbName, modelInfo := range dbName2ModelInfo {
			// 没过期
			if !modelInfo.CheckExpire(_nt) {
				continue
			}

			// 跨服redis
			if strings.Contains(dbName, "cross") {
				if !_crossRedisClear {
					_crossRedisClear = true
				}
			} else {
				// 本服redis
				if !_redisClear {
					_redisClear = true
				}
			}

			// 清除redis数据 这里用管道统一clear，避免频繁io
			modelInfo.Clear(uid)
			_delUid2DbNameList[uid] = append(_delUid2DbNameList[uid], dbName)
		}
	}
	m.mu.RUnlock()

	// 没有需要清除的数据
	if len(_delUid2DbNameList) == 0 {
		return
	}

	// 管道批量操作
	if _redisClear {
		Redis.Pipeline.Exec()
	}
	if _crossRedisClear {
		CrossRedis.Pipeline.Exec()
	}

	// 内存数据清除
	m.mu.Lock()
	for uid, dbNameList := range _delUid2DbNameList {
		for _, dbName := range dbNameList {
			// 删除单个表
			delete(m.uid2DbName2ModelBase[uid], dbName)
			// 一个表都没了，删掉玩家
			if len(m.uid2DbName2ModelBase[uid]) == 0 {
				delete(m.uid2DbName2ModelBase, uid)
				logger.Print("%s 所有model清除完毕", uid)
			}
		}
	}
	m.mu.Unlock()

}

// CheckClearModel 检测清除过期model
func (m *ManageStruct) CheckClearModel() {
	//_ticker := time.NewTicker(time.Minute)
	_ticker := time.NewTicker(time.Second * 5)
	defer _ticker.Stop()

	// 每隔1分钟检测1次
	for {
		select {
		case <-_ticker.C:
			m.clearModel()
		}
	}
}

// HasModelBase 是否存在该modelBase
func (m *ManageStruct) HasModelBase(uid string, modelBase *ModelBase) bool {
	m.mu.RLock()
	defer m.mu.RUnlock()

	_dbName2ModelInfo, ok := m.uid2DbName2ModelBase[uid]
	if !ok {
		return false
	}

	_, ok = _dbName2ModelInfo[modelBase.DbName]
	if !ok {
		return false
	}

	return true

}

// UpdateLastTime 更新modelInfo.LastTime
func (m *ManageStruct) UpdateLastTime(uid, dbName string) {
	m.mu.RLock()
	_dbName2ModelInfo, ok := m.uid2DbName2ModelBase[uid]
	if !ok {
		m.mu.RUnlock()
		return
	}

	_modelInfo, ok := _dbName2ModelInfo[dbName]
	m.mu.RUnlock()
	if !ok {
		return
	}

	m.mu.Lock()
	_modelInfo.LastTime = C.Now()
	//logger.Print("%s LastTime更新为%d", dbName, _modelInfo.LastTime)
	m.mu.Unlock()

	return

}

// AddModelBase 增加玩家modelBase
func (m *ManageStruct) AddModelBase(uid string, modelBase *ModelBase) {
	// 已添加
	if m.HasModelBase(uid, modelBase) {
		return
	}

	m.mu.Lock()
	defer m.mu.Unlock()

	_, ok := m.uid2DbName2ModelBase[uid]
	if !ok {
		m.uid2DbName2ModelBase[uid] = make(map[string]*ModelBase)

	}
	m.uid2DbName2ModelBase[uid][modelBase.DbName] = GenerateModelBase(modelBase)

}

// StopWriteModelLog 停止写入modelLog
func StopWriteModelLog() {
	if Manage.modelLogManage != nil {
		Manage.modelLogManage.StopWriteModelLog()
	}
	if Manage.modelLogCrossManage != nil {
		Manage.modelLogCrossManage.StopWriteModelLog()
	}
}
